#include <device.h>
#include <machine.h>
#include <uart.h>
#include <config.h>
#include <errno.h>
#include <init.h>
#include <memory.h>
#include <util.h>
#include <arch/io.h>
#include <arch/mmu.h>
#include <arch/cache.h>
#include <arch/cpu/soc/s3c2440.h>

#ifdef __TOP_HALF__

static void qt2440_sdram_init(void)
{
	struct s3c2440_mem * __maybe_unused memctl = s3c2440_get_base_memctl();
	
#if ((CONFIG_FCLK_FREQ == 304000000) && (CONFIG_FCLK_HCLK_RATIO == 3))
	writel((2 << 24) | (0 << 26) | (0 << 27) ,&memctl->bwscon);	// BANK6: 32bit, not wait, not UB/LB
	writel((3 << 15) | (1 << 2) | (1 << 0), &memctl->bankcon[6]);	// BANK6: SDRAM, 9bit column address, TRCD=3 clocks
	writel((1 << 23) | (0 << 22) | (1 << 20) | (0 << 18) | (1257 << 0), &memctl->refresh);
	writel((1 << 7) | (0 << 5) | (1 << 4) | (1 << 0), &memctl->banksize);
	writel((0 << 9) | (0 << 7) | (3 << 4) | (0 << 3) | (0 << 0), &memctl->mrsrb6);
#endif
}

extern void s3c2440_clocks_init(void);

static int qt2440_machine_init(void)
{
	struct device *dev;
	struct uart *uart_dev;

	dcache_disable();
	mmu_disable();
	icache_invalidate_all();
	icache_enable();

	s3c2440_clocks_init();
	qt2440_sdram_init();
	
	dev = device_lookup_by_id(DEVICE_UART, -1);
	if (IS_ERR(dev)) 
		return PTR_ERR(dev);

	uart_dev = dev->priv;
	uart_dev->init(dev);
	return 0;
}

#else 

static struct memory_bank qt2440_memory_banks[] = {
	{ .name = "BootSRAM",		.start_addr = 0,					.length = SZ_4K,				.type = MT_SRAM,	},
	{ .name = "SDRAM1",			.start_addr = 0x30000000,			.length = SZ_64M,				.type = MT_SDRAM,	},
	{ .name = "SFR_IRQ",		.start_addr = S3C2440_PA_IRQ,		.length = S3C2440_SZ_IRQ,		.type = MT_DEVICE,	},
	{ .name = "SFR_CLKPWR",		.start_addr = S3C2440_PA_CLKPWR,	.length = S3C2440_SZ_CLKPWR,	.type = MT_DEVICE,	},
	{ .name = "SFR_MEMCTRL",	.start_addr = S3C2440_PA_MEMCTRL,	.length = S3C2440_SZ_MEMCTRL,	.type = MT_DEVICE,	},
	{ .name = "SFR_GPIO",		.start_addr = S3C2440_PA_GPIO,		.length = S3C2440_SZ_GPIO,		.type = MT_DEVICE,	},
	{ .name = "SFR_UART",		.start_addr = S3C2440_PA_UART,		.length = S3C2440_SZ_UART,		.type = MT_DEVICE,	},
	{ .name = "SFR_TIMER",		.start_addr = S3C2440_PA_TIMER,		.length = S3C2440_SZ_TIMER,		.type = MT_DEVICE,	},
	{ .name = "SFR_RTC",		.start_addr = S3C2440_PA_RTC,		.length = S3C2440_SZ_RTC,		.type = MT_DEVICE,	},
	{ .name = "SFR_NAND",		.start_addr = S3C2440_PA_NAND,		.length = S3C2440_SZ_NAND,		.type = MT_DEVICE,	},
	{ .name = "SFR_WATCHDOG",	.start_addr = S3C2440_PA_WATCHDOG,	.length = S3C2440_SZ_WATCHDOG,	.type = MT_DEVICE,	},
};

static struct memory_zone qt2440_memory_zones[] = {
	{ .name = "SRAM",	.start_addr = 64,			.length = SZ_4K - SZ_64,			.type = MZ_NORMAL,	},
	{ .name = "SDRAM1",	.start_addr = 0x30080000,	.length = SZ_64M - SZ_512K,			.type = MZ_NORMAL,	},
};

static int qt2440_machine_init(void)
{
	struct s3c2440_gpio *gpio = s3c2440_get_base_gpio(); 
	struct memory_allocator *ma;
	unsigned int tmp;
	int ret, i;

	tmp  = readl(&gpio->gpfcon);
	tmp &= ~0xff00;
	tmp |= 0x5500;
	writel(tmp, &gpio->gpfcon);

	tmp  = readl(&gpio->gpfdat);
	tmp |= 0xf0;
	writel(tmp, &gpio->gpfdat);

	ret = memory_bank_add(qt2440_memory_banks, ARRAY_SIZE(qt2440_memory_banks));
	if (IS_ERR(ret))
		return ret;

	ma = memory_allocator_get("dlallocator");
	if (IS_ERR(ma)) 
		while (1);
	for (i = 0; i < ARRAY_SIZE(qt2440_memory_zones); i++) {
		qt2440_memory_zones[i].allocator = ma;
	}

	ret = memory_zone_add(qt2440_memory_zones, ARRAY_SIZE(qt2440_memory_zones));
	if (IS_ERR(ret))
		return ret;

	return 0;
}

#endif

MACHINE_START(QT2440)
	.cpu_val		= 0x41009200,
	.cpu_mask		= 0xff00fff0,
	.init_machine	= qt2440_machine_init,
MACHINE_END

